﻿using System;
using System.Globalization;
using System.Xml;
using dmoz.Entities;

namespace Dmoz.Importer
{
    /// <summary>
    /// Class that holds event arguments when parsing the RDF DMOZ file
    /// </summary>
    public class NodeLoadedEventArgs : EventArgs
    {
        private string _catid;
        private string _language;
        private string _title;
        private String _value;

        /// <summary>
        /// Initializes a new instance of the <see cref="NodeLoadedEventArgs"/> class.
        /// </summary>
        public NodeLoadedEventArgs()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NodeLoadedEventArgs"/> class.
        /// </summary>
        /// <param name="value">The value.</param>
        public NodeLoadedEventArgs(String value)
        {
            Value = value;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NodeLoadedEventArgs"/> class.
        /// </summary>
        /// <param name="language">The language.</param>
        /// <param name="value">The value.</param>
        public NodeLoadedEventArgs(String language, String value)
        {
            Value = value;
            Language = language;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NodeLoadedEventArgs"/> class.
        /// </summary>
        /// <param name="catid">The catid.</param>
        /// <param name="title">The title.</param>
        /// <param name="value">The value.</param>
        public NodeLoadedEventArgs(String catid, String title, String value)
        {
            Value = value;
            Catid = catid;
            Title = title;
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public String Value
        {
            get { return _value; }
            set { _value = value; }
        }

        /// <summary>
        /// Gets or sets the catid.
        /// </summary>
        /// <value>The catid.</value>
        public string Catid
        {
            get { return _catid; }
            set { _catid = value; }
        }

        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        /// <value>The title.</value>
        public string Title
        {
            get { return _title; }
            set { if (value != null) _title = (value.Length == 0 ? null : value); }
        }

        /// <summary>
        /// Gets or sets the language.
        /// </summary>
        /// <value>The language.</value>
        public string Language
        {
            get { return _language; }
            set { if (value != null) _language = (value.Length == 0 ? null : value); }
        }

        /// <summary>
        /// Toes the CSV string.
        /// </summary>
        /// <param name="resourceType">Type of the resource.</param>
        /// <param name="rowCount">The row count.</param>
        /// <returns></returns>
        public string ToCsvString(ResourceTypeList resourceType, int rowCount)
        {
            return String.Format(CultureInfo.InvariantCulture, "{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
                                 rowCount,
                                 _catid,
                                 (int) resourceType,
                                 _value,
                                 _title ?? String.Empty,
                                 _language ?? String.Empty
                );
        }


        /// <summary>
        /// Writes the XML.
        /// </summary>
        /// <param name="resourceType">Type of the resource.</param>
        /// <param name="xw">The xw.</param>
        /// <param name="rowCount">The row count.</param>
        public void WriteXml(ResourceTypeList resourceType, XmlWriter xw, int rowCount)
        {
            if (xw == null)
                throw new ArgumentNullException("xw");

            xw.WriteStartElement("Import");

            xw.WriteStartElement("idRow");
            xw.WriteValue(rowCount);
            xw.WriteEndElement();
            xw.WriteStartElement("catid");
            xw.WriteValue(Catid);
            xw.WriteEndElement();
            xw.WriteStartElement("resourcetype");
            xw.WriteValue((int) resourceType);
            xw.WriteEndElement();
            xw.WriteStartElement("value");
            xw.WriteValue(Value ?? String.Empty);
            xw.WriteEndElement();
            xw.WriteStartElement("title");
            xw.WriteValue(Title ?? String.Empty);
            xw.WriteEndElement();
            xw.WriteStartElement("language");
            xw.WriteValue(Language ?? String.Empty);
            xw.WriteEndElement();

            xw.WriteEndElement();
        }
    }

    internal class DmozRdfHelper
    {
        #region Variables

        private Uri _pathtoRdf;

        #endregion


        #region Events

        #region Delegates

        /// <summary>
        /// Event Handler for altlang elements
        /// </summary>
        public delegate void AltlangLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for editor elements
        /// </summary>
        public delegate void EditorLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for narrow elements
        /// </summary>
        public delegate void NarrowLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for newsgroup elements
        /// </summary>
        public delegate void NewsgroupLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for related elements
        /// </summary>
        public delegate void RelatedLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for symbolic elements
        /// </summary>
        public delegate void SymbolicLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for closing topic elements
        /// </summary>
        public delegate void TopicLeftEventHandler(object sender, NodeLoadedEventArgs args);

        /// <summary>
        /// Event Handler for topic elements
        /// </summary>
        public delegate void TopicLoadedEventHandler(object sender, NodeLoadedEventArgs args);

        #endregion

        /// <summary>
        /// Occurs when topic element loaded.
        /// </summary>
        public event TopicLoadedEventHandler TopicLoaded;

        /// <summary>
        /// Occurs when altlang element loaded.
        /// </summary>
        public event AltlangLoadedEventHandler AltlangLoaded;

        /// <summary>
        /// Occurs when narrow element loaded.
        /// </summary>
        public event NarrowLoadedEventHandler NarrowLoaded;

        /// <summary>
        /// Occurs when symbolic element loaded.
        /// </summary>
        public event SymbolicLoadedEventHandler SymbolicLoaded;

        /// <summary>
        /// Occurs when editor element loaded.
        /// </summary>
        public event EditorLoadedEventHandler EditorLoaded;

        /// <summary>
        /// Occurs when newsgroup element loaded.
        /// </summary>
        public event NewsgroupLoadedEventHandler NewsgroupLoaded;

        /// <summary>
        /// Occurs when related element loaded.
        /// </summary>
        public event RelatedLoadedEventHandler RelatedLoaded;

        /// <summary>
        /// Occurs when closing topic element loaded.
        /// </summary>
        public event TopicLeftEventHandler TopicLeft;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DmozRdfHelper"/> class.
        /// </summary>
        /// <param name="pathToRdf">The path to RDF.</param>
        public DmozRdfHelper(Uri pathToRdf)
        {
            _pathtoRdf = pathToRdf;
        }

        #endregion

        #region EventHandlers

        private void OnTopicLoaded(String catid, String title, String value)
        {
            TopicLoadedEventHandler handler = TopicLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(catid, title, value));
            }
        }

        private void OnNarrowLoaded(String value)
        {
            NarrowLoadedEventHandler handler = NarrowLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(value));
            }
        }

        private void OnSymbolicLoaded(String title, String value)
        {
            SymbolicLoadedEventHandler handler = SymbolicLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(null, title, value));
            }
        }

        private void OnEditorLoaded(String value)
        {
            EditorLoadedEventHandler handler = EditorLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(value));
            }
        }

        private void OnNewsgroupLoaded(String value)
        {
            NewsgroupLoadedEventHandler handler = NewsgroupLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(value));
            }
        }

        private void OnRelatedLoaded(String value)
        {
            RelatedLoadedEventHandler handler = RelatedLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(value));
            }
        }

        private void OnAltlangLoaded(String language, String value)
        {
            AltlangLoadedEventHandler handler = AltlangLoaded;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs(language, value));
            }
        }

        private void OnTopicLeft()
        {
            TopicLeftEventHandler handler = TopicLeft;
            if (handler != null)
            {
                handler(this, new NodeLoadedEventArgs());
            }
        }

        #endregion

        #region Parser

        /// <summary>
        /// Parses this instance.
        /// </summary>
        public void Parse()
        {
            XmlTextReader xr = new XmlTextReader(_pathtoRdf.ToString());
            xr.WhitespaceHandling = WhitespaceHandling.None;

            while (xr.Read())
            {
                if (xr.NodeType == XmlNodeType.EndElement)
                    switch (xr.Name.ToLower(CultureInfo.InvariantCulture))
                    {
                        case "topic":
                            OnTopicLeft();
                            break;
                    }

                if (xr.NodeType == XmlNodeType.Element)
                    switch (xr.Name.ToLower(CultureInfo.InvariantCulture))
                    {
                        case "topic":
                            String id, catid, title;

                            xr.MoveToAttribute("r:id");
                            id = xr.Value;

                            xr.Read(); // go to the <catid>
                            if (String.Compare(xr.Name, "catid", true, CultureInfo.InvariantCulture) != 0)
                                throw new XmlException(
                                    String.Format(CultureInfo.InvariantCulture, "there should be a <catid> element, I found a <{0}> !", xr.Name));
                            xr.Read(); //Move to the text content
                            catid = xr.Value;
                            xr.Read(); //Move to the </catid>

                            while ((String.Compare(xr.Name, "d:Title", true, CultureInfo.InvariantCulture) != 0)) xr.Read(); // go to the <d:Title>
                            xr.Read(); // move to the text content
                            title = xr.Value;
                            xr.Read(); //move to the </d:Title>

                            OnTopicLoaded(catid, title, id);
                            break;

                        case "narrow":
                        case "narrow2":
                        case "narrow1":
                            xr.MoveToAttribute("r:resource");
                            OnNarrowLoaded(xr.Value);
                            break;

                        case "altlang":
                            xr.MoveToAttribute("r:resource");
                            String[] r = xr.Value.Split(':');
                            OnAltlangLoaded(r[0], r[1]);
                            break;

                        case "symbolic":
                        case "symbolic1":
                        case "symbolic2":
                            xr.MoveToAttribute("r:resource");
                            String[] r2 = xr.Value.Split(':');
                            OnSymbolicLoaded(r2[0], r2[1]);
                            break;

                        case "editor":
                            xr.MoveToAttribute("r:resource");
                            OnEditorLoaded(xr.Value);
                            break;

                        case "newsgroup":
                            xr.MoveToAttribute("r:resource");
                            String[] r3 = xr.Value.Split(':');
                            OnNewsgroupLoaded(r3[1]);
                            break;

                        case "related":
                            xr.MoveToAttribute("r:resource");
                            OnRelatedLoaded(xr.Value);
                            break;
                    }
            }

            xr.Close();
        }

        #endregion
    }
}